﻿namespace LinkedInWinRT.Client
{
    using System;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Xml.Linq;
    using System.Threading.Tasks;
    using System.Collections.Generic;

    using Helpers;
    using Helpers.Models;

    public enum Permission
    {
        BasicProfile,
        FullProfile,
        EmailAddress,
        Network,
        Updates
    }

    public class LinkedInClient : LinkedInLoginHelper, ISocialClient
    {
        public LinkedInClient(string consumerKey, string consumerKeySecret, string callbackUrl)
        {
            Credentials = new Credentials
                {
                    ConsumerKey = consumerKey,
                    ConsumerKeySecret = consumerKeySecret,
                    CallbackUrl = callbackUrl
                };
            Permissions = new List<Permission>();
            PermissionsDictionary = new Dictionary<Permission, string>
                {
                    {Permission.BasicProfile, "r_basicprofile"},
                    {Permission.FullProfile, "r_fullprofile"},
                    {Permission.EmailAddress, "r_emailaddress"},
                    {Permission.Network, "r_network"},
                    {Permission.Updates, "rw_nus"}
                };
        }

        #region Properties

        public async Task<string> GetUpdates()
        {
            return await ApiRequestAsync(NetworkUpdates);
        }

        public Person AuthenticatedUser { get; set; }

        public bool IsLoggedIn { get; private set; }

        #endregion

        #region Methods

        /// <summary>
        /// This function will request permissions from the user
        /// such as email address, connections, etc.
        /// </summary>
        /// <param name="permissions">The permissions that you want from the user</param>
        /// <returns>A boolean value that indicates whether the function succeeded or not </returns>
        public async Task<bool> LoginAsync(List<Permission> permissions)
        {
            Permissions = permissions;
            return await LoginAsync();
        }

        /// <summary>
        /// Login process, using the WebAuthenticationBroker
        /// </summary>
        /// <returns>A boolean value that indicates whether the function succeeded or not </returns>
        public async Task<bool> LoginAsync()
        {
            try
            {
                await GetRequestToken();
                AuthenticatedUser = await GetUserInfoAsync();
                IsLoggedIn = true;
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Sets AccessToken and AccessTokenSecret to string.Empty
        /// </summary>
        public void Logout()
        {
            Credentials.AccessToken = string.Empty;
            Credentials.AccessTokenSecret = string.Empty;
        }


        /// <summary>
        /// Refresh the information from the AuthenticatedUser property
        /// This function will make an API call to LinkedIn in order
        /// to get the updated info
        /// </summary>
        /// <returns>A Person object with the updated information, or null if it fails</returns>
        public async Task<Person> RefreshUserInfoAsync()
        {
            var person = await GetUserInfoAsync();
            if (person != null)
            {
                AuthenticatedUser = person;
            }
            return person;
        }

        #endregion

        #region Helpers
        
        /// <summary>
        /// Get the user info associated with the current logged in user
        /// If the function fails, the return value is null
        /// </summary>
        /// <returns>An UserInfo object with all the information</returns>
        private async Task<Person> GetUserInfoAsync()
        {
            try
            {
                string profileXml =
                await
                ApiRequestAsync(
                    "http://api.linkedin.com/v1/people/~");
                Person person;
                // LinkedIn sends an XML file, so we must parse it and get the user info from it
                using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(profileXml)))
                {
                    XDocument doc = XDocument.Load(stream);
                    var personElement = doc.Descendants("person").FirstOrDefault();
                    if (personElement == null)
                    {
                        return null;
                    }
                    person = GetProfile(personElement);
                    if (Permissions.Contains(Permission.EmailAddress))
                    {
                        var email = await GetEmailAddressAsync();
                        person.EmailAddress = email;
                    }
                }
                if (Permissions.Contains(Permission.Network))
                {
                    person.Connections = await GetConnectionsAsync();
                }
                return person;
            }
            catch (Exception)
            {
                return null;
            }
        }

        private async Task<string> GetEmailAddressAsync()
        {
            try
            {
                string emailXml = await ApiRequestAsync("http://api.linkedin.com/v1/people/~:(email-address)");
                using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(emailXml)))
                {
                    XDocument doc = XDocument.Load(stream);
                    XElement emailElement = doc.Descendants("email-address").FirstOrDefault();
                    string emailAddress = null;
                    if (emailElement != null)
                        emailAddress = emailElement.Value;
                    return emailAddress.IsValidEmail() ? emailAddress : string.Empty;
                }
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }
        
        private Person GetProfile(XElement personElement)
        {
            var person = new Person();
            try
            {
                var idElement = personElement.Element("id");
                if (idElement != null)
                {
                    person.Id = idElement.GetElementValue();
                }

                person.FirstName = personElement.Element("first-name").GetElementValue();
                person.LastName = personElement.Element("last-name").GetElementValue();
                person.Headline = personElement.Element("headline").GetElementValue();
                person.PictureUrl = personElement.Element("picture-url").GetElementValue();
                person.Industry = personElement.Element("industry").GetElementValue();

                var siteStandardProfileRequestElement = personElement.Element("site-standard-profile-request");
                if (siteStandardProfileRequestElement != null)
                {
                    person.ProfileUrl = siteStandardProfileRequestElement.Element("url").GetElementValue();
                }

                var locationElement = personElement.Element("site-standard-profile-request");
                if (locationElement != null)
                {
                    person.Location = locationElement.Element("url").GetElementValue();
                }
            }
            catch (Exception)
            {
                return new Person();
            }
            return person;
        }

        private async Task<List<Person>> GetConnectionsAsync()
        {
            var connections = new List<Person>();
            try
            {
                string profileXml =
                await
                ApiRequestAsync(
                    RequestConnections);
                using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(profileXml)))
                {
                    var document = XDocument.Load(stream);

                    var personElements = document.Descendants("person");

                    connections.AddRange(personElements.Select(GetProfile));
                }
            }
            catch (Exception exception)
            {
                return null;
            }
            return connections;
        }

        #endregion

      
    }
}